// axiosUtils.ts
import axios from 'axios'
import type { AxiosRequestConfig, AxiosResponse, Canceler } from 'axios'
import { local } from '@/utils/storage' // 导入之前创建的存储工具

// 请求取消器存储映射
const pendingRequests = new Map<string, Canceler>()

// 是否正在刷新token
let isRefreshing = false
// 等待token刷新的请求队列
let refreshSubscribers: Array<(token: string) => void> = []
// 获取环境变量
const BASE_API = import.meta.env.VITE_BASE_API
// 设置baseURL
const baseURL = import.meta.env.MODE === 'production' ? BASE_API : ''

// 默认配置
const defaultConfig: AxiosRequestConfig = {
  timeout: 10000,
  baseURL,
  headers: {
    'Content-Type': 'application/json;charset=utf-8',
  },
}

/**
 * 生成请求的唯一键
 * @param config 请求配置
 * @returns 唯一键
 */
const generateRequestKey = (config: AxiosRequestConfig): string => {
  const { url, method, params, data } = config
  return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&')
}

/**
 * 添加请求取消器
 * @param config 请求配置
 */
const addPendingRequest = (config: AxiosRequestConfig): void => {
  const requestKey = generateRequestKey(config)
  config.cancelToken = new axios.CancelToken((cancel) => {
    if (!pendingRequests.has(requestKey)) {
      pendingRequests.set(requestKey, cancel)
    }
  })
}

/**
 * 移除请求取消器
 * @param config 请求配置
 */
const removePendingRequest = (config: AxiosRequestConfig): void => {
  const requestKey = generateRequestKey(config)
  if (pendingRequests.has(requestKey)) {
    pendingRequests.delete(requestKey)
  }
}

/**
 * 取消所有请求
 */
export const cancelAllRequests = (): void => {
  pendingRequests.forEach((cancel) => {
    cancel('请求被取消')
  })
  pendingRequests.clear()
}

/**
 * 将请求添加到刷新订阅者队列中
 * @param callback 回调函数，接收新token
 */
const subscribeTokenRefresh = (callback: (token: string) => void) => {
  refreshSubscribers.push(callback)
}

/**
 * 执行队列中的请求，使用新的token
 * @param token 新token
 */
const onTokenRefreshed = (token: string) => {
  refreshSubscribers.forEach((callback) => callback(token))
  refreshSubscribers = []
}

/**
 * 刷新token
 * @returns 新的token
 */
const refreshToken = async (): Promise<string> => {
  try {
    // 从localStorage获取刷新token
    const refreshToken = local.get<string>('refreshToken')
    if (!refreshToken) {
      throw new Error('没有可用的刷新token')
    }

    // 创建一个新的axios实例用于刷新token请求
    // 这样可以避免进入拦截器的循环
    const refreshInstance = axios.create({
      baseURL: defaultConfig.baseURL,
      timeout: defaultConfig.timeout,
    })

    const response = await refreshInstance.post<
      ApiResponse<{ token: string; refreshToken: string }>
    >('/auth/refresh-token', { refreshToken })

    if (response.data.success) {
      const newToken = response.data.data.token
      const newRefreshToken = response.data.data.refreshToken

      // 存储新的token和refreshToken
      local.set('token', newToken)
      local.set('refreshToken', newRefreshToken)

      return newToken
    } else {
      throw new Error(response.data.message)
    }
  } catch (error) {
    // 刷新token失败，清除所有认证信息并跳转到登录页
    local.remove('token')
    local.remove('refreshToken')
    local.remove('userInfo')

    // 这里可以添加跳转到登录页的逻辑，例如：
    // window.location.href = '/login';

    throw error
  }
}

/**
 * 创建Axios实例
 */
const instance = axios.create(defaultConfig)

// 请求拦截器
instance.interceptors.request.use(
  (config) => {
    // 取消之前相同的请求
    removePendingRequest(config)
    // 添加新的请求取消器
    addPendingRequest(config)

    // 从localStorage获取token并添加到请求头
    const token = local.get<string>('token')
    if (token && config.headers) {
      config.headers.Authorization = `Bearer ${token}`
    }

    return config
  },
  (error) => {
    return Promise.reject(error)
  },
)

// 响应拦截器
instance.interceptors.response.use(
  (response) => {
    // 请求完成后移除请求取消器
    removePendingRequest(response.config)

    // 这里可以根据业务需求处理响应数据
    // 例如：只返回response.data，或者根据状态码进行处理
    return response
  },
  async (error) => {
    // 如果请求被取消，不做特殊处理
    if (axios.isCancel(error)) {
      return Promise.reject(error)
    }

    // 请求出错也需要移除请求取消器
    if (error.config) {
      removePendingRequest(error.config)
    }

    // 处理token过期的情况
    if (error.response && error.response.status === 401) {
      // 获取原始请求配置
      const originalRequest = error.config

      // 避免重复刷新token
      if (!originalRequest._retry) {
        // 如果token正在刷新中，将请求添加到队列
        if (isRefreshing) {
          return new Promise((resolve) => {
            subscribeTokenRefresh((token: string) => {
              // 使用新token更新请求头
              originalRequest.headers.Authorization = `Bearer ${token}`
              // 重试原始请求
              resolve(instance(originalRequest))
            })
          })
        }

        // 标记该请求正在重试
        originalRequest._retry = true
        isRefreshing = true

        try {
          // 刷新token
          const newToken = await refreshToken()

          // 通知所有等待的请求
          onTokenRefreshed(newToken)

          // 使用新token更新请求头
          originalRequest.headers.Authorization = `Bearer ${newToken}`

          // 重试原始请求
          return instance(originalRequest)
        } catch (refreshError) {
          // 刷新token失败，拒绝所有等待的请求
          refreshSubscribers.forEach((callback) => callback(''))
          refreshSubscribers = []

          return Promise.reject(refreshError)
        } finally {
          isRefreshing = false
        }
      }
    }

    // 处理其他错误状态码
    handleHttpError(error)

    return Promise.reject(error)
  },
)

/**
 * 处理HTTP错误
 * @param error 错误对象
 */
const handleHttpError = (error: any): void => {
  if (error.response) {
    switch (error.response.status) {
      case 400:
        console.error('请求参数错误')
        break
      case 403:
        // 权限不足
        console.error('权限不足')
        break
      case 404:
        // 资源不存在
        console.error('请求资源不存在')
        break
      case 500:
        // 服务器错误
        console.error('服务器错误')
        break
      default:
        console.error(`未知错误: ${error.response.status}`)
    }
  } else if (error.request) {
    // 请求已发送但没有收到响应
    console.error('网络错误或服务器未响应')
  } else {
    // 请求配置有误
    console.error('请求配置错误:', error.message)
  }
}

/**
 * 请求接口
 */
export interface RequestOptions extends AxiosRequestConfig {
  skipErrorHandler?: boolean // 是否跳过错误处理
  cache?: boolean // 是否缓存请求结果
  cacheKey?: string // 缓存键名
  cacheDuration?: number // 缓存时间(毫秒)
  skipTokenRefresh?: boolean // 是否跳过token刷新
  returnFullResponse?: boolean // 是否返回完整的 Axios 响应对象
}

/**
 * 响应结构接口
 */
export interface ApiResponse<T = any> {
  code: number
  data: T
  message: string
  success: boolean
}

/**
 * 发送GET请求
 * @param url 请求地址
 * @param params URL参数
 * @param options 请求选项
 * @returns Promise
 */
export const get = <T = any>(
  url: string,
  params?: any,
  options?: RequestOptions,
): Promise<ApiResponse<T> | AxiosResponse<ApiResponse<T>>> => {
  // 检查是否使用缓存
  if (options?.cache) {
    const cacheKey = options.cacheKey || `api-cache:${url}:${JSON.stringify(params)}`
    const cachedData = local.get<{ data: ApiResponse<T>; timestamp: number }>(cacheKey)

    // 如果有缓存且未过期，则直接返回缓存数据
    if (cachedData && options.cacheDuration) {
      const now = Date.now()
      if (now - cachedData.timestamp < options.cacheDuration) {
        const response = {
          data: cachedData.data,
          status: 200,
          statusText: 'OK (cached)',
          headers: {},
          config: {},
        } as AxiosResponse<ApiResponse<T>>

        return Promise.resolve(options?.returnFullResponse ? response : response.data)
      }
    }
  }

  return instance
    .get<ApiResponse<T>>(url, {
      params,
      ...options,
    })
    .then((response) => {
      // 如果需要缓存，则存储响应数据
      if (options?.cache) {
        const cacheKey = options.cacheKey || `api-cache:${url}:${JSON.stringify(params)}`
        local.set(cacheKey, {
          data: response.data,
          timestamp: Date.now(),
        })
      }
      return options?.returnFullResponse ? response : response.data
    })
}

/**
 * 发送POST请求
 * @param url 请求地址
 * @param data 请求体数据
 * @param options 请求选项
 * @returns Promise
 */
export const post = <T = any>(
  url: string,
  data?: any,
  options?: RequestOptions,
): Promise<ApiResponse<T> | AxiosResponse<ApiResponse<T>>> => {
  return instance
    .post<ApiResponse<T>>(url, data, options)
    .then((response) => (options?.returnFullResponse ? response : response.data))
}

/**
 * 发送PUT请求
 * @param url 请求地址
 * @param data 请求体数据
 * @param options 请求选项
 * @returns Promise
 */
export const put = <T = any>(
  url: string,
  data?: any,
  options?: RequestOptions,
): Promise<ApiResponse<T> | AxiosResponse<ApiResponse<T>>> => {
  return instance
    .put<ApiResponse<T>>(url, data, options)
    .then((response) => (options?.returnFullResponse ? response : response.data))
}

/**
 * 发送DELETE请求
 * @param url 请求地址
 * @param params URL参数
 * @param options 请求选项
 * @returns Promise
 */
export const del = <T = any>(
  url: string,
  params?: any,
  options?: RequestOptions,
): Promise<ApiResponse<T> | AxiosResponse<ApiResponse<T>>> => {
  return instance
    .delete<ApiResponse<T>>(url, {
      params,
      ...options,
    })
    .then((response) => (options?.returnFullResponse ? response : response.data))
}

/**
 * 发送PATCH请求
 * @param url 请求地址
 * @param data 请求体数据
 * @param options 请求选项
 * @returns Promise
 */
export const patch = <T = any>(
  url: string,
  data?: any,
  options?: RequestOptions,
): Promise<ApiResponse<T> | AxiosResponse<ApiResponse<T>>> => {
  return instance
    .patch<ApiResponse<T>>(url, data, options)
    .then((response) => (options?.returnFullResponse ? response : response.data))
}

/**
 * 取消指定URL的请求
 * @param url 请求地址
 * @param method 请求方法
 * @param params URL参数
 * @param data 请求体数据
 */
export const cancelRequest = (
  url: string,
  method: string = 'get',
  params?: any,
  data?: any,
): void => {
  const requestKey = generateRequestKey({
    url,
    method,
    params,
    data,
  })

  if (pendingRequests.has(requestKey)) {
    const cancel = pendingRequests.get(requestKey)
    cancel?.('请求被手动取消')
    pendingRequests.delete(requestKey)
  }
}

/**
 * 设置请求默认配置
 * @param config 配置对象
 */
export const setDefaultConfig = (config: AxiosRequestConfig): void => {
  Object.assign(defaultConfig, config)

  // 更新实例配置 - 类型安全的方式
  ;(Object.entries(config) as [keyof AxiosRequestConfig, any][]).forEach(([key, value]) => {
    if (key in instance.defaults) {
      instance.defaults[key] = value
    }
  })
}

/**
 * 创建请求控制器
 * @returns 请求控制器对象
 */
export const createRequestController = () => {
  const controller = new AbortController()
  const { signal } = controller

  return {
    signal,
    abort: () => controller.abort(),
  }
}

/**
 * 上传文件
 * @param url 上传地址
 * @param file 文件对象
 * @param name 文件字段名
 * @param data 其他表单数据
 * @param options 请求选项
 * @returns Promise
 */
export const uploadFile = <T = any>(
  url: string,
  file: File,
  name: string = 'file',
  data?: Record<string, any>,
  options?: RequestOptions,
): Promise<ApiResponse<T> | AxiosResponse<ApiResponse<T>>> => {
  const formData = new FormData()
  formData.append(name, file)

  if (data) {
    Object.entries(data).forEach(([key, value]) => {
      formData.append(key, value)
    })
  }

  return instance
    .post<ApiResponse<T>>(url, formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
      ...options,
    })
    .then((response) => (options?.returnFullResponse ? response : response.data))
}

/**
 * 下载文件
 * @param url 下载地址
 * @param params URL参数
 * @param filename 保存的文件名
 * @param options 请求选项
 * @returns Promise
 */
export const downloadFile = (
  url: string,
  params?: any,
  filename?: string,
  options?: RequestOptions,
): Promise<void> => {
  return instance
    .get(url, {
      params,
      responseType: 'blob',
      ...options,
    })
    .then((response) => {
      // 创建下载链接
      const blob = new Blob([response.data])
      const downloadUrl = URL.createObjectURL(blob)
      const link = document.createElement('a')
      link.href = downloadUrl

      // 获取文件名
      let saveFilename = filename
      if (!saveFilename) {
        // 尝试从响应头获取文件名
        const contentDisposition = response.headers['content-disposition']
        if (contentDisposition) {
          const filenameMatch = contentDisposition.match(/filename="?([^"]*)"?/)
          if (filenameMatch && filenameMatch[1]) {
            saveFilename = filenameMatch[1]
          }
        }
        // 如果仍然没有文件名，使用当前时间戳
        if (!saveFilename) {
          saveFilename = `download_${Date.now()}`
        }
      }

      link.download = saveFilename
      link.click()

      // 清理
      URL.revokeObjectURL(downloadUrl)
    })
}

/**
 * 手动刷新token
 * @returns 刷新后的token
 */
export const refreshTokenManually = async (): Promise<string> => {
  if (isRefreshing) {
    // 如果正在刷新，返回一个Promise等待刷新完成
    return new Promise((resolve) => {
      subscribeTokenRefresh((token: string) => {
        resolve(token)
      })
    })
  }

  isRefreshing = true
  try {
    const newToken = await refreshToken()
    onTokenRefreshed(newToken)
    return newToken
  } catch (error) {
    throw error
  } finally {
    isRefreshing = false
  }
}

/**
 * 检查并设置token
 * @param token 访问令牌
 * @param refreshTokenValue 刷新令牌
 */
export const setAuthTokens = (token: string, refreshTokenValue: string): void => {
  local.set('token', token)
  local.set('refreshToken', refreshTokenValue)
}

/**
 * 清除认证信息
 */
export const clearAuthTokens = (): void => {
  local.remove('token')
  local.remove('refreshToken')
  local.remove('userInfo')
}

/**
 * 获取当前token
 * @returns 当前token
 */
export const getToken = (): string | null => {
  return local.get<string>('token')
}

/**
 * 获取当前刷新token
 * @returns 当前刷新token
 */
export const getRefreshToken = (): string | null => {
  return local.get<string>('refreshToken')
}

/**
 * 检查是否已认证
 * @returns 是否已认证
 */
export const isAuthenticated = (): boolean => {
  return !!getToken()
}

// 导出axios实例，以便进行更高级的自定义
export { instance as axios }
// 导出默认对象
export default {
  get,
  post,
  put,
  del,
  patch,
  cancelRequest,
  cancelAllRequests,
  setDefaultConfig,
  createRequestController,
  uploadFile,
  downloadFile,
  refreshTokenManually,
  setAuthTokens,
  clearAuthTokens,
  getToken,
  getRefreshToken,
  isAuthenticated,
  axios: instance,
}
